#include "ssl.h"
#include "string.h"
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/aes.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h> /* For htonl() */


/* OpenSSL Functions -------------------------------------------*/
void init_ssl(void)
{
#if 1    
    if(SSL_library_init())
    {
        SSL_load_error_strings();
        OpenSSL_add_all_algorithms();
        RAND_load_file("/dev/urandom", 1024);
    }
    else
        exit(EXIT_FAILURE);
#endif    
}

void cleanup_openssl(void)
{
#if 1    
    CRYPTO_cleanup_all_ex_data();
    ERR_free_strings();
    ERR_remove_thread_state(0);
    EVP_cleanup();
#endif    
}

void handle_openssl_error(void)
{
    #if 1
    ERR_print_errors_fp(stderr);
    #endif
}
/* OpenSSL Functions -------------------------------------------END */

/* Config Init Functions --------------------------START */
ssl_ret_t set_keyStore_path(ssl_t *this, char *keyStorePath);
ssl_ret_t set_root_ca_cert(ssl_t *this, char *root_ca_cert);

ssl_ret_t set_keyStore_path(ssl_t *this, char *keyStorePath)
{
    strcpy(this->keyStorePath, keyStorePath);
    return SSL_SUCCESS;
}
ssl_ret_t set_root_ca_cert(ssl_t *this, char *root_ca_cert)
{
    strcpy(this->root_ca_cert, root_ca_cert);
    return SSL_SUCCESS;
}

/* Config Init Functions --------------------------END */



/* Symmetric Key Functions ------------------START */
ssl_ret_t ssl_init_aes_encrypt_decrypt_contexts(ssl_t *this);
ssl_ret_t ssl_deinit_aes_encrypt_decrypt_contexts(ssl_t *this);
ssl_ret_t ssl_gen_aes_keys(ssl_t *this, unsigned char **aesKey, 
    unsigned char **aesIV);
int writeAESKeyToFile(FILE *fd, int key, void *key1, void *key2);

ssl_ret_t ssl_init_aes_encrypt_decrypt_contexts(ssl_t *this)
{
    
    // Check if already inited
    if((this->aesEncryptCtx != NULL) || 
       (this->aesDecryptCtx != NULL)) {
        return SSL_ERROR;
    }

    
    this->aesEncryptCtx = (EVP_CIPHER_CTX*)malloc(sizeof(EVP_CIPHER_CTX));
    this->aesDecryptCtx = (EVP_CIPHER_CTX*)malloc(sizeof(EVP_CIPHER_CTX));

    // Null checks for malloc
    if((this->aesEncryptCtx == NULL) || 
       (this->aesDecryptCtx == NULL)) {
        return SSL_ERROR;
    }

    // Initialise the contexts
    EVP_CIPHER_CTX_init(this->aesEncryptCtx);
    EVP_CIPHER_CTX_init(this->aesDecryptCtx);
    return SSL_SUCCESS;
}


ssl_ret_t ssl_deinit_aes_encrypt_decrypt_contexts(ssl_t *this)
{
    EVP_CIPHER_CTX_cleanup(this->aesEncryptCtx);
    EVP_CIPHER_CTX_cleanup(this->aesDecryptCtx);
    free(this->aesEncryptCtx);
    free(this->aesDecryptCtx);
    this->aesEncryptCtx=NULL;
    this->aesDecryptCtx=NULL;
    return SSL_SUCCESS;    
}
 
ssl_ret_t ssl_gen_aes_keys(ssl_t *this, unsigned char **aesKey, 
    unsigned char **aesIV)
{
    // Init AES
    *aesKey = (unsigned char*)malloc(AES_KEYLEN/8);
    *aesIV = (unsigned char*)malloc(AES_KEYLEN/8);

    unsigned char *aesPass = (unsigned char*)malloc(AES_KEYLEN/8);
    unsigned char *aesSalt = (unsigned char*)malloc(8);

    if(*aesKey == NULL || *aesIV == NULL || 
       aesPass == NULL || aesSalt == NULL) {
        return SSL_ERROR;
    }

    // Get some random data to use as the AES pass and salt
    if(RAND_bytes(aesPass, AES_KEYLEN/8) == 0) {
        return SSL_ERROR;
    }

    if(RAND_bytes(aesSalt, 8) == 0) {
        return SSL_ERROR;
    }

    if(EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha256(), 
        aesSalt, aesPass, AES_KEYLEN/8, AES_ROUNDS, *aesKey, *aesIV) == 0) {
        return SSL_ERROR;
    }

    free(aesPass);
    free(aesSalt);

    return SSL_SUCCESS;
}


int writeAESKeyToFile(FILE *fd, int key, void *key1, void *key2)
{
    unsigned char *aesKey;
    unsigned char *aesIV;


    switch(key) {
    case KEY_AES_KEY:
    {
        aesKey = key1;
        fwrite(aesKey, 1, AES_KEYLEN, fd);
    }
    break;
    case KEY_AES_IV:
    {
        aesIV = key2;
        fwrite(aesIV, 1, AES_KEYLEN, fd);
    }
    break;
    default:
        return SSL_ERROR;
    }
    return SSL_SUCCESS;
}

/* Symmetric Key Functions ------------------ END */



/* Asymmetric RSA Public Key/Private Key Functions ---------- START */

ssl_ret_t ssl_init_rsa_encrypt_decrypt_contexts(ssl_t *this);
ssl_ret_t ssl_deinit_rsa_encrypt_decrypt_contexts(ssl_t *this);
ssl_ret_t ssl_gen_rsa_keys(ssl_t *this, EVP_PKEY **keypair);
ssl_ret_t writeRSAKeyToFile(FILE *fd, int key, void *key1, 
                      char *pass);
ssl_ret_t ssl_write_rsa_keys_to_file(ssl_t *this, 
                char *my_pub_key, char *my_priv_key, 
                char *my_priv_key_password,
                EVP_PKEY *pkey);
ssl_ret_t ssl_write_rsa_keys_to_file(ssl_t *this, 
                char *my_pub_key, char *my_priv_key, 
                char *my_priv_key_password,
                EVP_PKEY *pkey);
ssl_ret_t create_my_encryption_keys(ssl_t *this, char *my_enc_pub_key,
             char *my_enc_priv_key, char *my_enc_priv_key_password);
ssl_ret_t create_my_signing_keys(ssl_t *this, char *my_sign_pub_key,
             char *my_sign_priv_key, char *my_sign_priv_key_password);
ssl_ret_t read_rsa_key_pair(ssl_t *this, 
            char *pub_key_fname,
            char *priv_key_fname, 
            char *password,EVP_PKEY **pPrivKey,
            EVP_PKEY **pPubKey);
ssl_ret_t read_my_encryption_keys(ssl_t *this, char *my_enc_pub_key,
             char *my_enc_priv_key, char *my_enc_priv_key_password);
ssl_ret_t read_my_signing_keys(ssl_t *this, char *my_sign_pub_key,
             char *my_sign_priv_key, char *my_sign_priv_key_password);


ssl_ret_t ssl_init_rsa_encrypt_decrypt_contexts(ssl_t *this)
{
    
    // Check if already inited
    if((this->rsaEncryptCtx != NULL) || 
       (this->rsaDecryptCtx != NULL))
    {
        return SSL_ERROR;
    }

    
    this->rsaEncryptCtx = (EVP_CIPHER_CTX*)malloc(sizeof(EVP_CIPHER_CTX));
    this->rsaDecryptCtx = (EVP_CIPHER_CTX*)malloc(sizeof(EVP_CIPHER_CTX));

    // Null checks for malloc
    if ((this->rsaEncryptCtx == NULL) || 
        (this->rsaDecryptCtx == NULL)) {
        return SSL_ERROR;
    }

    // Initialise the contexts
    EVP_CIPHER_CTX_init(this->rsaEncryptCtx);
    EVP_CIPHER_CTX_init(this->rsaDecryptCtx);
    return SSL_SUCCESS;

}


ssl_ret_t ssl_deinit_rsa_encrypt_decrypt_contexts(ssl_t *this)
{
    EVP_CIPHER_CTX_cleanup(this->rsaEncryptCtx);
    EVP_CIPHER_CTX_cleanup(this->rsaDecryptCtx);
    free(this->rsaEncryptCtx);
    free(this->rsaDecryptCtx);
    this->rsaEncryptCtx=NULL;
    this->rsaDecryptCtx=NULL;
        return SSL_SUCCESS;
}

ssl_ret_t ssl_gen_rsa_keys(ssl_t *this, EVP_PKEY **keypair)
{
    // Init RSA
    EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);

    if(EVP_PKEY_keygen_init(ctx) <= 0) {
        return SSL_ERROR;
    }

    if(EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, RSA_KEYLEN) <= 0) {
        return SSL_ERROR;
    }

    if(EVP_PKEY_keygen(ctx, keypair) <= 0) {
        return SSL_ERROR;
    }

    EVP_PKEY_CTX_free(ctx);
        return SSL_SUCCESS;    
}

ssl_ret_t writeRSAKeyToFile(FILE *fd, int key, void *key1, 
                      char *pass) 
{
    EVP_PKEY *rsaKeypair;
    EVP_PKEY *rsaPubKey;
    int ret;

    switch(key) {
    case KEY_PAIR_RSA_PRI:
    { 
        rsaKeypair = (EVP_PKEY *) key1;
#if 1        
        if (NULL != pass)
        ret = PEM_write_PrivateKey(fd, rsaKeypair,
                 EVP_aes_256_cbc(),(unsigned char *)pass,strlen(pass),
                 NULL, NULL);
        else
#endif            
        ret = PEM_write_PrivateKey(fd, rsaKeypair,
                                NULL, NULL, 0,
                                0, NULL);
        if(!ret) {
            return SSL_ERROR;
        }
    }
    break;
    case KEY_PAIR_RSA_PUB:
    {
        rsaKeypair = (EVP_PKEY *) key1;    
        if(!PEM_write_PUBKEY(fd, rsaKeypair)) {
            return SSL_ERROR;
        }
    }
    break;
    case KEY_SINGLE_RSA_PUB:
    {
        rsaPubKey = (EVP_PKEY *) key1;
        if(!PEM_write_PUBKEY(fd, rsaPubKey)) {
            return SSL_ERROR;
        }
    }
    break;
    default:
        return SSL_ERROR;
    }
    return SSL_SUCCESS;
}

ssl_ret_t ssl_write_rsa_keys_to_file(ssl_t *this, 
                char *my_pub_key, char *my_priv_key, 
                char *my_priv_key_password,
                EVP_PKEY *pkey)
{
    int c;    
    /* Open the PEM file for writing the key to disk. */
    FILE *pub_key_file, *priv_key_file;
    char full_path[256];

    memset(full_path, 0, 256);
    strncpy(full_path, this->keyStorePath, 256);
    c = strlen(full_path);
    strncat(full_path, "/", (256-c)?(256-c):0);
    c = strlen(full_path);
    strncat(full_path, my_pub_key, (256-c)?(256-c):0);
    pub_key_file = fopen(full_path, "wb");
    if(!pub_key_file)
    {
        fprintf(stderr,"Unable to open \"%s\" for writing.\n",
                full_path );
        return SSL_ERROR;
    }


    memset(full_path, 0, 256);
    strncpy(full_path, this->keyStorePath, 256);
    c = strlen(full_path);
    strncat(full_path, "/", (256-c)?(256-c):0);
    c = strlen(full_path);
    strncat(full_path, my_priv_key, (256-c)?(256-c):0);
    priv_key_file = fopen(full_path, "wb");
    if(!priv_key_file)
    {
        fprintf(stderr,"Unable to open \"%s\" for writing.",
            full_path );
        return SSL_ERROR;
    }
    
    writeRSAKeyToFile(priv_key_file, KEY_PAIR_RSA_PRI,
                pkey,my_priv_key_password);
    writeRSAKeyToFile(pub_key_file, KEY_PAIR_RSA_PUB, 
                pkey,my_priv_key_password);
    
    fclose(priv_key_file);
    fclose(pub_key_file);
    return SSL_SUCCESS; 
}


ssl_ret_t create_my_encryption_keys(ssl_t *this, char *my_enc_pub_key,
             char *my_enc_priv_key, char *my_enc_priv_key_password)
{
    ssl_ret_t ret;
	
    strcpy(this->my_enc_pub_key, my_enc_pub_key);
    strcpy(this->my_enc_priv_key, my_enc_priv_key);
    strcpy(this->my_enc_priv_key_password, my_enc_priv_key_password);
    ret = ssl_gen_rsa_keys(this,&this->enc_key);
    ret = ssl_write_rsa_keys_to_file(this, my_enc_pub_key,
             my_enc_priv_key, my_enc_priv_key_password,
                this->enc_key);
    return ret;
}


ssl_ret_t create_my_signing_keys(ssl_t *this, char *my_sign_pub_key,
             char *my_sign_priv_key, char *my_sign_priv_key_password)
{
    ssl_ret_t ret;
	
    strcpy(this->my_sign_pub_key, my_sign_pub_key);
    strcpy(this->my_sign_priv_key, my_sign_priv_key);
    strcpy(this->my_sign_priv_key_password, my_sign_priv_key_password);
    ret = ssl_gen_rsa_keys(this,&this->sign_key);
    ret = ssl_write_rsa_keys_to_file(this, my_sign_pub_key,
             my_sign_priv_key, my_sign_priv_key_password,
   	     this->sign_key);
    return ret;
}

ssl_ret_t read_rsa_pub_key(ssl_t *this, 
            char *pub_key_fname,
            EVP_PKEY **pPubKey)
{
    FILE*     pFile    = NULL;
    char full_path[256];
    int c;

    *pPubKey = NULL;
    
    if (pub_key_fname) {
        
    memset(full_path, 0, 256);
    strncpy(full_path, this->keyStorePath, 256);
    c = strlen(full_path);
    strncat(full_path, "/", (256-c)?(256-c):0);
    c = strlen(full_path);
    strncat(full_path, pub_key_fname, (256-c)?(256-c):0);  
    
    pFile = fopen(full_path,"rt");
    
    if(pFile) {   
        *pPubKey = PEM_read_PUBKEY(pFile,NULL,NULL,NULL);
        if (*pPubKey)
        {
            fprintf(stderr,"Public key read.\n");
        }
        else
        {
            fprintf(stderr,"Cannot read public key.\n");
            return SSL_ERROR;
        }
        
        fclose(pFile);
        pFile = NULL;
    }
    }
    
    return SSL_SUCCESS;
}
        
ssl_ret_t read_rsa_key_pair(ssl_t *this, 
            char *pub_key_fname,
            char *priv_key_fname, 
            char *password,EVP_PKEY **pPrivKey,
            EVP_PKEY **pPubKey)
{
    FILE*     pFile    = NULL;
    char full_path[256];
    int c;

    *pPubKey = NULL;
    *pPrivKey  = NULL;
    if (priv_key_fname) {
        
    memset(full_path, 0, 256);
    strncpy(full_path, this->keyStorePath, 256);
    c = strlen(full_path);
    strncat(full_path, "/", (256-c)?(256-c):0);
    c = strlen(full_path);
    strncat(full_path, priv_key_fname, (256-c)?(256-c):0);  
    
    pFile = fopen(full_path,"rt");
    
    if(pFile) {
        *pPrivKey = PEM_read_PrivateKey(pFile,NULL,NULL,
                            (void*)password);
    
        if (*pPrivKey)
        {
            fprintf(stderr,"Private key read.\n");
        }
        else
        {
            fprintf(stderr,"Cannot read private key.\n");
            return SSL_ERROR;
        }
        fclose(pFile);
        pFile = NULL;
    }
    }
    
    if (pub_key_fname) {
        
    memset(full_path, 0, 256);
    strncpy(full_path, this->keyStorePath, 256);
    c = strlen(full_path);
    strncat(full_path, "/", (256-c)?(256-c):0);
    c = strlen(full_path);
    strncat(full_path, pub_key_fname, (256-c)?(256-c):0);  
    
    pFile = fopen(full_path,"rt");
    
    if(pFile) {   
        *pPubKey = PEM_read_PUBKEY(pFile,NULL,NULL,NULL);
        if (*pPubKey)
        {
            fprintf(stderr,"Public key read.\n");
        }
        else
        {
            fprintf(stderr,"Cannot read public key.\n");
            return SSL_ERROR;
        }
        
        fclose(pFile);
        pFile = NULL;
    }
    }
    
    return SSL_SUCCESS;
}

ssl_ret_t read_rsa_key_pair(ssl_t *this, 
            char *pub_key_fname,
            char *priv_key_fname, 
            char *password,EVP_PKEY **pPrivKey,
            EVP_PKEY **pPubKey);

ssl_ret_t read_my_encryption_keys(ssl_t *this, char *my_enc_pub_key,
             char *my_enc_priv_key, char *my_enc_priv_key_password)
{
    ssl_ret_t ret;
	
    strcpy(this->my_enc_pub_key, my_enc_pub_key);
    strcpy(this->my_enc_priv_key, my_enc_priv_key);
    strcpy(this->my_enc_priv_key_password, my_enc_priv_key_password);
    ret = read_rsa_key_pair(this, my_enc_pub_key,
             my_enc_priv_key, my_enc_priv_key_password,
   	     &this->enc_priv_key, &this->enc_pub_key);
    return ret;
}


ssl_ret_t read_my_signing_keys(ssl_t *this, char *my_sign_pub_key,
             char *my_sign_priv_key, char *my_sign_priv_key_password)
{
    ssl_ret_t ret;
	
    strcpy(this->my_sign_pub_key, my_sign_pub_key);
    strcpy(this->my_sign_priv_key, my_sign_priv_key);
    strcpy(this->my_sign_priv_key_password, my_sign_priv_key_password);
    ret = ssl_gen_rsa_keys(this,&this->sign_key);
    ret = read_rsa_key_pair(this, my_sign_pub_key,
             my_sign_priv_key, my_sign_priv_key_password,
   	     &this->sign_priv_key, &this->sign_pub_key);
    return ret;
}

ssl_ret_t destroy_signing_key(ssl_t *this)
{
    EVP_PKEY_free(this->sign_key);
    return SSL_SUCCESS;
}

ssl_ret_t destroy_encryption_key(ssl_t *this)
{
    EVP_PKEY_free(this->enc_key);
    return SSL_SUCCESS;
}
/* Asymmetric RSA Public Key/Private Key Functions ---------- END */

/* Encode/Decode Functions -------------------------- START */
ssl_ret_t decode_rsa_message(ssl_t *this,EVP_PKEY *key,
                    unsigned char *buffer, int bufsize, 
                    unsigned char **decode_buffer, int *decode_bufsize);
ssl_ret_t encode_rsa_message(ssl_t *this,EVP_PKEY *key,
                    unsigned char *buffer, int bufsize, 
                    unsigned char **encode_buffer, int *encode_bufsize);
ssl_ret_t decode_aes_message(ssl_t *this,unsigned char *aesKey,
                    unsigned char *aesIV,
                    unsigned char *buffer, int bufsize, 
                    unsigned char **decode_buffer, int *decode_bufsize);
ssl_ret_t encode_aes_message(ssl_t *this,unsigned char *aesKey, 
                    unsigned char *aesIV, 
                    unsigned char *buffer, int bufsize, 
                    unsigned char **encode_buffer, int *encode_bufsize);

ssl_ret_t decode_rsa_message(ssl_t *this,EVP_PKEY *key,
                    unsigned char *buffer, int bufsize, 
                    unsigned char **decode_buffer, int *decode_bufsize)
{
    size_t decLen = 0;
    size_t blockLen = 0;
    unsigned char *ek = NULL;
    int eklen;
    uint32_t eklen_n, ivlen;
    unsigned char *iv;
    unsigned char *buff_in;
    int header_len;
    
    ek = (unsigned char*)malloc(EVP_PKEY_size(key));
    iv = (unsigned char*)malloc(EVP_MAX_IV_LENGTH);
    
    if(ek == NULL || iv == NULL) 
        return SSL_ERROR;
    
    ivlen = EVP_MAX_IV_LENGTH;    
    
    
    
    /* First need to fetch the encrypted key length, encrypted key and IV */
    header_len = 0;
    buff_in = buffer;
    memcpy(&eklen_n, buff_in,sizeof(eklen_n));
    eklen = ntohl(eklen_n);
    buff_in +=  sizeof(eklen_n);
    header_len += sizeof(eklen_n);
    if (eklen > EVP_PKEY_size(key))
    {
        fprintf(stderr, "Bad encrypted key length (%u > %d)\n", eklen,
            EVP_PKEY_size(key));
        return SSL_ERROR;
    }
    buff_in += sizeof(eklen_n);
    memcpy(ek, buff_in, eklen);
    buff_in += eklen;
    header_len += eklen;
    ivlen = EVP_CIPHER_iv_length(EVP_aes_256_cbc());
    memcpy(iv, buff_in,ivlen);
    buff_in+=ivlen;
    header_len += ivlen;
        
    
    *decode_buffer = (unsigned char*)malloc(bufsize+ivlen);
    if(*decode_buffer == NULL) return SSL_ERROR;

    if(!EVP_OpenInit(this->rsaDecryptCtx, 
            EVP_aes_256_cbc(), ek, eklen, iv, key)) {
            return SSL_ERROR;
    }
    
    if(!EVP_OpenUpdate(this->rsaDecryptCtx,
        (unsigned char*)*decode_buffer + decLen,
        (int*)&blockLen, 
        buff_in, (int)(bufsize-header_len))) {
        return SSL_ERROR;
    }
    
    decLen += blockLen;
    
    if(!EVP_OpenFinal(this->rsaDecryptCtx, 
            (unsigned char*)*decode_buffer + decLen, 
            (int*)&blockLen)) {
        return SSL_ERROR;
    }
    
    decLen += blockLen;
    *decode_bufsize = decLen;
    free(ek);
    free(iv);
    return SSL_SUCCESS;
}

ssl_ret_t encode_rsa_message(ssl_t *this,EVP_PKEY *key,
                    unsigned char *buffer, int bufsize, 
                    unsigned char **encode_buffer, int *encode_bufsize)
{
    size_t encMsgLen = 0;
    size_t blockLen = 0;
    unsigned char *ek = NULL;
    int eklen;
    uint32_t eklen_n, ivlen;
    unsigned char *iv;
    unsigned char *buff_out;
    int header_len;
    
    ek = (unsigned char*)malloc(EVP_PKEY_size(key));
    iv = (unsigned char*)malloc(EVP_MAX_IV_LENGTH);
    
    if(ek == NULL || iv == NULL) {
        return SSL_ERROR;
    }
    
    ivlen = EVP_MAX_IV_LENGTH;
    
    *encode_buffer = (unsigned char*)malloc(bufsize 
                                + EVP_MAX_IV_LENGTH);
    
    if(*encode_buffer == NULL) {
           return SSL_ERROR;
    }
    
    if(!EVP_SealInit(this->rsaEncryptCtx, EVP_aes_256_cbc(), 
            &ek, &eklen, iv, &key, 1)) {
        fprintf(stderr, "EVP_SealInit: failed.\n");
        return SSL_ERROR;
    }
    header_len = 0;
    eklen_n = htonl(eklen);
    buff_out = *encode_buffer;
    /* symmetric keylen */
    memcpy(buff_out, &eklen_n,sizeof(eklen_n));
    buff_out +=sizeof(eklen_n);
    header_len += sizeof(eklen_n);
    /* symmetric key */
    memcpy(buff_out,ek, eklen);
    buff_out +=eklen;
    header_len += eklen;
    /* iv */
    ivlen = EVP_CIPHER_iv_length(EVP_aes_256_cbc());
    memcpy(buff_out, iv, ivlen);
    buff_out +=ivlen;
    header_len += ivlen;
    
    if(!EVP_SealUpdate(this->rsaEncryptCtx,
                buff_out + encMsgLen, 
                (int*)&blockLen, (const unsigned char*)buffer, 
                (int)bufsize)) {
        return SSL_ERROR;
    }
    buff_out += blockLen;
    encMsgLen += blockLen;
    
    if(!EVP_SealFinal(this->rsaEncryptCtx, 
        buff_out, 
        (int*)&blockLen)) {
        return SSL_ERROR;
    }
    
    encMsgLen += blockLen;
    
    *encode_bufsize = encMsgLen+header_len;
    free(ek);
    free(iv);
    return SSL_SUCCESS;
}


ssl_ret_t decode_aes_message(ssl_t *this,unsigned char *aesKey,
                    unsigned char *aesIV,
                    unsigned char *buffer, int bufsize, 
                    unsigned char **decode_buffer, int *decode_bufsize)
{
    size_t decLen = 0;
    size_t blockLen = 0;

    *decode_buffer = (unsigned char*)malloc(bufsize);
    if(*decode_buffer == NULL) 
            return SSL_ERROR;
        
    if(!EVP_DecryptInit_ex(this->aesDecryptCtx, EVP_aes_256_cbc(), 
        NULL, aesKey, aesIV)) {
        return SSL_ERROR;
    }
    
    if(!EVP_DecryptUpdate(this->aesDecryptCtx,
            (unsigned char*)*decode_buffer,
            (int*)&blockLen, buffer, (int)bufsize)) {
        return SSL_ERROR;
    }
    
    decLen += blockLen;
    
    if(!EVP_DecryptFinal_ex(this->aesDecryptCtx, 
            (unsigned char*)*decode_buffer + decLen, 
            (int*)&blockLen)) {
        return SSL_ERROR;
    }
    
    decLen += blockLen;
    *decode_bufsize = decLen;
    return SSL_SUCCESS;
}


ssl_ret_t encode_aes_message(ssl_t *this,unsigned char *aesKey, 
                    unsigned char *aesIV, 
                    unsigned char *buffer, int bufsize, 
                    unsigned char **encode_buffer, int *encode_bufsize)
{
    size_t blockLen = 0;
    size_t encMsgLen = 0;
    
    *encode_buffer = (unsigned char*)malloc(bufsize + AES_BLOCK_SIZE);
    if(*encode_buffer == NULL) 
        return SSL_ERROR;
    
    if(!EVP_EncryptInit_ex(this->aesEncryptCtx, EVP_aes_256_cbc(), 
        NULL, aesKey, aesIV)) {
        return SSL_ERROR;
    }
    
    if(!EVP_EncryptUpdate(this->aesEncryptCtx, *encode_buffer,
        (int*)&blockLen, (unsigned char*)buffer, bufsize)) {
        return SSL_ERROR;
    }
    
    encMsgLen += blockLen;
    
    if(!EVP_EncryptFinal_ex(this->aesEncryptCtx,
        *encode_buffer + encMsgLen, 
            (int*)&blockLen)) {
        return SSL_ERROR;
    }
    
    *encode_bufsize = encMsgLen + blockLen;
    return SSL_SUCCESS;
}
/* Encode/Decode Functions -------------------------- END */

/* Certificate Functions -------------------------- START*/
X509 * generate_x509(ssl_t *this, EVP_PKEY * pkey);
ssl_ret_t create_my_cert(ssl_t *this, char *my_cert);
ssl_ret_t destroy_my_cert(ssl_t *this);

/* Generates a self-signed x509 certificate. */
X509 * generate_x509(ssl_t *this, EVP_PKEY * pkey)
{
    /* Allocate memory for the X509 structure. */
    X509 * x509 = X509_new();
    if(!x509)
    {
        fprintf(stderr,"Unable to create X509 structure."  "\n" );
        return NULL;
    }
    
    /* Set the serial number. */
    ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
    
    /* This certificate is valid from now until exactly one year from now. */
    X509_gmtime_adj(X509_get_notBefore(x509), 0);
    X509_gmtime_adj(X509_get_notAfter(x509), 31536000L);
    
    /* Set the public key for our certificate. */
    X509_set_pubkey(x509, pkey);
    
    /* We want to copy the subject name to the issuer name. */
    X509_NAME * name = X509_get_subject_name(x509);
    
    /* Set the country code and common name. */
    X509_NAME_add_entry_by_txt(name, "C",  MBSTRING_ASC, (unsigned char *)"CA",        -1, -1, 0);
    X509_NAME_add_entry_by_txt(name, "O",  MBSTRING_ASC, (unsigned char *)"EcmFs", -1, -1, 0);
    X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)"localhost", -1, -1, 0);
    
    /* Now set the issuer name. */
    X509_set_issuer_name(x509, name);
    
    /* Actually sign the certificate with our key. */
    if(!X509_sign(x509, pkey, EVP_sha1()))
    {
        fprintf(stderr,"Error signing certificate."  "\n" );
        X509_free(x509);
        return NULL;
    }
    
    return x509;
}
 

ssl_ret_t create_my_cert(ssl_t *this, char *my_cert)
{
    FILE *cert;
    int ret = 0;
    char full_path[256];
    int c;
    if (!this->enc_key) {
        fprintf(stderr,"key pair not available"  "\n" );
        return SSL_ERROR;
    }

    strcpy(this->my_cert, my_cert);

    memset(full_path, 0, 256);
    strncpy(full_path, this->keyStorePath, 256);
    c = strlen(full_path);
    strncat(full_path, "/", (256-c)?(256-c):0);
    c = strlen(full_path);
    strncat(full_path, this->my_cert, (256-c)?(256-c):0);

    cert = fopen(full_path, "wb");
    if(!cert)
    {
        fprintf(stderr,"Unable to open \"%s\" for writing.\n",
                full_path);
        return SSL_ERROR;
    }

    this->my_x509_cert = generate_x509(this, this->enc_key);
    if(!this->my_x509_cert)
    {
        fprintf(stderr,"Unable to open create cert."  "\n" );
        return SSL_ERROR;
    }

    ret = PEM_write_X509(cert, this->my_x509_cert);
    
    if(!ret)
    {
        fprintf(stderr,"Unable to write certificate to disk."  "\n" );
        fclose(cert);
        return SSL_ERROR;
    }

    fclose(cert);
    return SSL_SUCCESS;
}

ssl_ret_t destroy_my_cert(ssl_t *this)
{
    if(this->my_x509_cert)
    X509_free(this->my_x509_cert);
    return SSL_SUCCESS;
}

/* GET/SET Functions ----------------------------------------------*/
#if 0
ssl_ret_t getRemotePubKey(unsigned char **pubKey) {
    int pubKeyLen
    BIO *bio;
    
    bio= BIO_new(BIO_s_mem());
    
    PEM_write_bio_PUBKEY(bio, remtePubKey);
    
    pubKeyLen = BIO_pending(bio);
    
    *pubKey = (unsigned char*)malloc(pubKeyLen);
    
    if(pubKey == NULL) 
        return FAILURE;
    
    BIO_read(bio, *pubKey, pubKeyLen);
    
    // Insert the NUL terminator
    (*pubKey)[pubKeyLen-1] = '\0';
    
    BIO_free_all(bio);
    return pubKeyLen;
}

ssl_ret_t setRemotePubKey(unsigned char* pubKey, size_t pubKeyLen)
{
    //BIO *bio = BIO_new(BIO_f_base64());
    BIO *bio = BIO_new(BIO_s_mem());
    
    if(BIO_write(bio, pubKey, pubKeyLen)
        != (int)pubKeyLen) {
        return FAILURE;
    }
    
    RSA *_pubKey = (RSA*)malloc(sizeof(RSA));
    
    if(_pubKey == NULL) {
            return FAILURE;
    }
    
    PEM_read_bio_PUBKEY(bio, &remotePubKey, NULL, NULL);
    BIO_free_all(bio);
    return SUCCESS;
}

ssl_ret_t getLocalPubKey(unsigned char** pubKey)
{
    BIO *bio;
    int pubKeyLen;
    
    bio= BIO_new(BIO_s_mem());
    
    PEM_write_bio_PUBKEY(bio, localKeypair);
    
    pubKeyLen = BIO_pending(bio);
    
    *pubKey = (unsigned char*)malloc(pubKeyLen);
    
    if(pubKey == NULL) {
        return FAILURE;
    }
    
    BIO_read(bio, *pubKey, pubKeyLen);
    
    // Insert the NUL terminator
    (*pubKey)[pubKeyLen-1] = '\0';
    
    BIO_free_all(bio);
    
    return pubKeyLen;
}

ssl_ret_t getLocalPriKey(unsigned char **priKey)
{
    BIO *bio;
    int priKeyLen;
    
    bio = BIO_new(BIO_s_mem());
    
    PEM_write_bio_PrivateKey(bio, localKeypair, 
        NULL, NULL, 0, 0, NULL);
    
    priKeyLen = BIO_pending(bio);
    *priKey = (unsigned char*)malloc(priKeyLen + 1);
    if(priKey == NULL) 
        return FAILURE;
    BIO_read(bio, *priKey, priKeyLen);
    
    // Insert the NUL terminator
    (*priKey)[priKeyLen] = '\0';
    BIO_free_all(bio);
    return priKeyLen;
}

int getAESKey(unsigned char **aesKey)
{
*aesKey = this->aesKey;
return AES_KEYLEN/8;
}

int setAESKey(unsigned char *aesKey, size_t aesKeyLen)
{
    if((int)aesKeyLen 
            != AES_KEYLEN/8)
    {
        return FAILURE;
    }
    memcpy(this->aesKey, aesKey, AES_KEYLEN/8);
    return SUCCESS;
}

int getAESIv(unsigned char **aesIV)
{
    *aesIV = this->aesIV;
    return AES_KEYLEN/16;
}

int setAESIv(unsigned char *aesIV, size_t aesIVLen)
{
    // Ensure the new IV is the proper size
    if((int)aesIVLen != AES_KEYLEN/16) {
        return FAILURE;
    }
    memcpy(this->aesIV, aesIV, AES_KEYLEN/16);
    return SUCCESS;
}
#endif


/* Read Keys ------------------------------------------------------*/






/* UNUSED FUNCTIONS; Can be use LATER -----------------------------*/

/* Generates a 2048-bit RSA key. */
#if 0




EVP_PKEY* create_rsa_key(void)
{
    RSA *pRSA      = NULL;
    EVP_PKEY* pKey = NULL;
    pRSA = RSA_generate_key(2048,RSA_3,gen_callback,NULL);
    pKey = EVP_PKEY_new();
    if(pRSA && pKey && EVP_PKEY_assign_RSA(pKey,pRSA))
    {
        /* pKey owns pRSA from now */
        if(RSA_check_key(pRSA) <= 0)
        {
            fprintf(stderr,"RSA_check_key failed.\n");
            handle_openssl_error();
            EVP_PKEY_free(pKey);
            pKey = NULL;
        }
    }
    else
    {
        handle_openssl_error();
        if(pRSA)
        {
            RSA_free(pRSA);
            pRSA = NULL;
        }
        if(pKey)
        {
            EVP_PKEY_free(pKey);
            pKey = NULL;
        }
    }
    return pKey;
}

void gen_callback(int iWhat, int inPrime, void* pParam)
{
    char c='*';
    switch(iWhat)
    {
        case 0: c = '.';  break;
        case 1: c = '+';  break;
        case 2: c = '*';  break;
        case 3: c = '\n'; break;
    }
    fprintf(stderr,"%c",c);
}

int passwd_callback(char *pcszBuff,int size,int rwflag, void *pPass)
{
    size_t unPass = strlen((char*)pPass);
    if(unPass > (size_t)size)
        unPass = (size_t)size;
    memcpy(pcszBuff, pPass, unPass);
    return (int)unPass;
}

EVP_PKEY * generate_key(ssl_t *this)
{
    BIGNUM          *bne = NULL;
    int             bits = 2048;
    unsigned long   e = RSA_F4;
    int ret = 0;
    /* Allocate memory for the EVP_PKEY structure. */
    EVP_PKEY * pkey = EVP_PKEY_new();
    if(!pkey)
    {
        fprintf(stderr,"Unable to create EVP_PKEY structure."  "\n" );
        return NULL;
    }

    bne = BN_new();
    ret = BN_set_word(bne,e);
    if(ret != 1){
        return NULL;
    }  
    /* Generate the RSA key and assign it to pkey. */
    RSA * rsa = RSA_new();
    RSA_generate_key_ex(rsa, bits, bne, NULL);
    if(!EVP_PKEY_assign_RSA(pkey, rsa))
    {
        fprintf(stderr,"Unable to generate 2048-bit RSA key."  "\n" );
        EVP_PKEY_free(pkey);
        BN_free(bne);
        return NULL;
    }
    BN_free(bne);
    /* The key has been generated, return it. */
    return pkey;
}

ssl_ret_t create_key_pair(ssl_t *this, char *my_pub_key,
                char *my_priv_key, char *my_priv_key_password,
		EVP_PKEY **pkey)
{
    /* Open the PEM file for writing the key to disk. */
    FILE *pub_key_file, *priv_key_file;
    bool_ecmfs ret;
    char full_path[256];

    memset(full_path, 0, 256);
    strncpy(full_path, this->keyStorePath, 256);
    strncat(full_path, my_pub_key, 256);
    pub_key_file = fopen(full_path, "wb");
    if(!pub_key_file)
    {
        fprintf(stderr,"Unable to open \"key.pem\" for writing."  "\n" );
        return SSL_ERROR;
    }


    memset(full_path, 0, 256);
    strncpy(full_path, this->keyStorePath, 256);
    strncat(full_path, my_priv_key, 256);
    priv_key_file = fopen(full_path, "wb");
    if(!priv_key_file)
    {
        fprintf(stderr,"Unable to open \"key.pem\" for writing."  "\n" );
        return SSL_ERROR;
    }

    *pkey = generate_key(this);
    if(!(*pkey))
    {
        fprintf(stderr,"Unable to generate key."  "\n" );
        return SSL_ERROR;
    }

    /* Write the key to disk. */
#if 0
    if (NULL != my_priv_key_password)
    ret = PEM_write_PrivateKey(priv_key_file, *pkey, EVP_aes_256_cbc(),
						(unsigned char *)my_priv_key_password,
						 strlen(my_priv_key_password), NULL, NULL);
    else
#endif
    ret = PEM_write_PrivateKey(priv_key_file, *pkey, NULL,NULL,0, NULL, NULL);

    fclose(priv_key_file);
    
    if(!ret)
    {
        fprintf(stderr,"Unable to write private key to disk."  "\n" );
        return SSL_ERROR;
    }

    ret = PEM_write_PUBKEY(pub_key_file, *pkey);
    fclose(pub_key_file);

    
    if(!ret)
    {
        fprintf(stderr,"Unable to write private key to disk."  "\n" );
        return SSL_ERROR;
    }

    return SSL_SUCCESS;
}

int pass_cb_enc_key(ssl_t *this, char *buf, int size, int rwflag, void *u)
{
int len;
/* We'd probably do something else if 'rwflag' is 1 */
len = strlen(this->my_enc_priv_key_password);
if (len <= 0) return 0;
/* if too long, truncate */
if (len > size) len = size;
memcpy(buf, this->my_enc_priv_key_password, len);
return len;
}


int pass_cb_sign_key(ssl_t *this, char *buf, int size, int rwflag, void *u)
{
int len;
/* We'd probably do something else if 'rwflag' is 1 */
len = strlen(this->my_sign_priv_key_password);
if (len <= 0) return 0;
/* if too long, truncate */
if (len > size) len = size;
memcpy(buf, this->my_sign_priv_key_password, len);
return len;
}
#endif



